unit oSimulation;

interface

uses
  uOSPABA,
  oAgentBoss,
  oAgentEnviroment,
  oAgentLevel,
  oAgentTerminal,
  oAgentTourniquet,
  oPedActor,
  oArrivalsManager,
  oScene,
  oStatisticProcessor,
  Generics.Collections,
  oStatisticEventConstants,
  oRunPropertiesManager,
  Classes,
  oLevel,
  uospstat,
  oStats,
  ComCtrls,
  StdCtrls,
  oFrameScene;

const
  //stats IDs
  SpentTime = 0;
  PeopleInModel = 1;
  ArrivalCount = 2;

  //kody pre ID agentov
  IDAgentModelu = 1000;
  IDAgentOkolia = 2000;
  IDAgentTerminalu = 3000;
  IDAgentPrvkov = 4000;
  IDAgentMobilnychPrvkov = 5000;
  IDAgentPohybu = 6000;
  IDAgentPoschodia = 8881;
  IDAgentVytahov = 8882;
  IDAgentSchodov = 8883;
  IDAgentPlosin = 8884;
  IDAgentTurniketov = 8885;
  IDAgentPokladnic = 8886;
  IDAgentDveri = 8887;
  IDAgentTabuli = 8888;

  //kody pre ID manazerov
  IDManazerModelu = 1001;
  IDManazerOkolia = 2001;
  IDManazerTerminalu = 3001;
  IDManazerPrvkov = 4001;
  IDManazerMobilnychPrvkov = 5001;
  IDManazerPohybu = 6001;
  IDManazerPoschodia = 9991;
  IDManazerVytahov = 9992;
  IDManazerSchodov = 9993;
  IDManazerPlosin = 9994;
  IDManazerTurniketov = 9995;
  IDManazerPokladnic = 9996;
  IDManazerDveri = 9997;
  IDManazerTabuli = 9998;

  //kody pre ID asistentov
  IDAsistentModelu = 1002;
  IDPlanovacPrichoduChodcov = 2002;
  IDPlanovacNasadenaiVozidiel = 5002;
  IDAsistentPohybuVozidiel = 6002;
  IDAsistentPoschodi = 9901;
  IDAsistentVytahov = 9902;
  IDAsistentSchodov = 9903;
  IDAsistentPlosin = 9904;
  IDAsistentTuniketov = 9905;
  IDAsistentPokladnic = 9906;
  IDAsistentDveri = 9907;
  IDAsistentTabul = 9908;

  //actor
  IDActor = 7770;
  IDActorManager = 7771;

  //kody sprav
  mcInit = 100000;
  mcInitO = 100001;
  mcInitV = 100002;
  mcPrichodCM = 100003;
  mcOdchodCM = 100004;
  mcPrichodCT = 100005;
  mcOdchodCT = 100006;
  mcPrichodCP = 100007;
  mcOdchodCP = 100008;
  mcPrichodC = 100009;
  mcNovy = 100010;
  mcObsluzeny = 100011;
  mcPrejstTurniket = 100012;
  mcPresielTurniket = 100013;
  mcSpomal = 100014;

  //typy actorov
  atBA = 0;
  atSS = 1;

  // tick duration
  TickDuration = 0.5;

type
  TTextFile = class
    public
      text : text;
  end;

  TSimLogger = class
    private
      aStrings: TStrings;
    public
      constructor create(paStrings: TStrings);
      procedure add(paMessage: String);
      procedure clear;
  end;

  TSimGUI = class;

  TPedSimulation = class(TSimulation)
    private
      aAgentBoss: TAgentBoss;
      aAgentEnviroment: TAgentEnviroment;
      aAgentTerminal: TAgentTerminal;
      aAgentLevel: TAgentLevel;
      aAgentTourniquet: TAgentTourniquet;
      aArrivals: TList<TArrivalsManager.TArrivalType>;
      aRunProperties: TRunPropertiesManager.TRunPropertiesType;
      aLogger: TSimLogger;
      aWorld: TScene;
      aLength: Currency;
      aReplication: Integer;
      aSimGUI: TSimGUI;
      aPedCountInModel: TXYStat;
      aStatisticDir : string;
      aStatisticFiles : TList<TTextFile>;
      aStatisticFlags : TList<Boolean>;
    public
      property SimGUI: TSimGUI read aSimGUI;
      property StatisticDir : string read aStatisticDir write aStatisticDir;
      property RunProperties: TRunPropertiesManager.TRunPropertiesType read aRunProperties;
      property AgentBoss:TAgentBoss read aAgentBoss;
      property AgentEnviroment:TAgentEnviroment read aAgentEnviroment;
      property AgentTerminal: TAgentTerminal read aAgentTerminal;
      property AgentLevel: TAgentLevel read aAgentLevel;
      property AgentTourniquet: TAgentTourniquet read aAgentTourniquet;
      property Arrivals: TList<TArrivalsManager.TArrivalType> read aArrivals;
      property World: TScene read aWorld;
      property Length:Currency read aLength;
      property StatPeopleInModel: TXYStat read aPedCountInModel;
      constructor Create(paWorld: TScene;
                         paArrivals: TList<TArrivalsManager.TArrivalType>;
                         paRunProperties: TRunPropertiesManager.TRunPropertiesType;
                         paSimGUI: TSimGUI= nil;
                         paStrings: TStrings = nil);
      destructor Destroy; override;
      procedure Simulation;
      procedure Continue;
      procedure OnTimeAdvance; override;
      procedure logMessage(paMessage: String);
      procedure prepareStatFiles(paStats : Tlist<Boolean>; paNames : Tlist<TStringIntegerPair>);
      procedure closeStatFiles;
      procedure openStatFiles;
      procedure writeStaticticEvent(paType : integer; paId : integer; paValue : real); overload;
      procedure writeStaticticEvent(paType : integer; paValue : real); overload;
      procedure writeStaticticEvent(paType : integer; paName : string; paValue : real); overload;
      procedure writeStaticticEvent(paType : integer; paMean, paDispersion : real); overload;
  end;

  TPSMessage = class(TMessageForm)
    private
      //atributy, ktore sa budu v sprave posielat
      aActor: TPedActor;
      aID: Integer;
      aArrival: TArrivalsManager.TArrivalType;
    public
      property Actor: TPedActor read aActor write aActor;
      property Arrival: TArrivalsManager.TArrivalType read aArrival write aArrival;
      constructor Create(paSim: TSimulation; paActor: TPedActor);
      destructor Destroy; override;
      property ID: Integer read aID write aID;
      function CreateForm: TMessageForm; override;
      procedure MakeCopy(paSample: TMessageForm); override;
  end;

  TSimGUI = class
    strict private
      aMySim: tSimulation;
      aSleepEvery: Integer;
      aSleepTime: Integer;
      aSleepTimeMax: Integer;
      aSleepEveryMax: Integer;
      aOutput: Boolean;
      aDrawFrame: TfEditor;
      aBar: TProgressBar;
      aTime: TLabel;
      function getSleepEvery: Cardinal;
      function getSleepTime: Cardinal;
      function getVypisovat: Boolean;
      procedure setSleepEvery(Value: Cardinal);
      procedure setSleepTime(Value: Cardinal);
      procedure setVypisovat(Value: Boolean);
    public
      property MySim: tSimulation read aMySim write aMySim;
      property ZaspatKazdych: Cardinal read getSleepEvery write setSleepEvery;
      property ZaspatNa: Cardinal read getSleepTime write setSleepTime;
      property Vypisovat: Boolean read getVypisovat write setVypisovat;
      constructor create(paSim: tSimulation;
                         paDrawFrame: TfEditor;
                         paBar: TProgressBar;
                         paTime: TLabel;
                         paSleepTimeMax: Integer = 20;
                         paSleepTime: Integer = 20;
                         paSleepEveryMax: Integer = 20;
                         paSleepEvery: Integer = 0;
                         paOutput: Boolean = true);
      destructor Destroy; override;
      procedure Step;
      procedure IncTime;
      procedure DecTime;
      procedure IncEvery;
      procedure DecEvery;
      class function convertTime(paTime: Double): String;
  end;

implementation

uses Dialogs,Forms,SysUtils, fmain, oPedestrian;

{ TPedSim }

constructor TPedSimulation.Create(paWorld: TScene;
                                  paArrivals: TList<TArrivalsManager.TArrivalType>;
                                  paRunProperties: TRunPropertiesManager.TRunPropertiesType;
                                  paSimGUI: TSimGUI = nil;
                                  paStrings: TStrings = nil);
begin
  inherited create;
  aSimGUI := paSimGUI;
  aSimGUI.MySim := Self;
  aPedCountInModel := nil;
  if paStrings = nil
    then aLogger := nil
    else aLogger := TSimLogger.create(paStrings);
  aRunProperties := paRunProperties;
  aWorld := paWorld;
  aArrivals := paArrivals;
  aAgentBoss := TAgentBoss.Create(IDAgentModelu,self,nil);
  aAgentEnviroment := TAgentEnviroment.create(IDAgentOkolia,self,AgentBoss);
  aAgentTerminal := TAgentTerminal.create(IDAgentTerminalu,self,AgentBoss);
  aAgentLevel := TAgentLevel.Create(IDAgentPoschodia,self,AgentTerminal);
  aAgentTourniquet := TAgentTourniquet.create(IDAgentTurniketov,self,AgentLevel);
  aLength := 0;
  aReplication := 0;

  aStatisticFiles := TList<TTextFile>.Create;
  RegisterForm(TPSMessage.create(self,nil));
end;

destructor TPedSimulation.destroy;
var
  level : TLevel;
begin
  for level in aWorld.Levels do
   level.AllPedestrians.Clear;
  aWorld := nil;
  aArrivals := nil;
  aStatisticFiles.Free;
  aPedCountInModel.Free;
  inherited;
end;

procedure TPedSimulation.prepareStatFiles;
var
  pf : TTextFile;
  j: Integer;
begin
  for j := 0 to paNames.Count - 1 do
      begin
       pf := TTextFile.Create;
       AssignFile(pf.text, StatisticDir + '/' + paNames[j].Key + '.txt');
       Rewrite(pf.text);
       aStatisticFiles.Add(pf);
    end;
  aStatisticFlags := paStats;
end;

procedure TPedSimulation.onTimeAdvance;
begin
  //SimGUI.Step;
  inherited;
end;

procedure TPedSimulation.openStatFiles;
var
  j : integer;
begin
  for j := 0 to aStatisticFiles.Count - 1 do
//    if aStatisticFlags[j] then
      Reset(aStatisticFiles[j].text);
end;

procedure TPedSimulation.closeStatFiles;
var
  j : integer;
begin
  for j := 0 to aStatisticFiles.Count - 1 do
//    if aStatisticFlags[j] then
    CloseFile(aStatisticFiles[j].text);
end;

procedure TPedSimulation.writeStaticticEvent(paType : integer; paName : string; paValue : real);
var
  index : integer;
begin
  index := -1;
  case paType of
    SE_WAITING_TIME : index := 0;
    SE_PEOPLE_IN_ARRIVAL : index := 2;
  end;
  if index <> -1 then
   if aStatisticFlags[index] then
    Writeln(aStatisticFiles[index].text, paName + ':' + FloatToStr(paValue));
end;

procedure TPedSimulation.writeStaticticEvent(paType : integer; paId : integer; paValue : real);
var
  index : integer;
begin
  index := -1;
  case paType of
    SE_WAITING_TIME : index := 0;
  end;
  if index <> -1 then
   if aStatisticFlags[index] then
      Writeln(aStatisticFiles[index].text, IntToStr(paId) +
          ':' + FloatToStr(paValue));
end;

procedure TPedSimulation.writeStaticticEvent(paType : integer; paValue : real);
var
  index : integer;
begin
  index := -1;
  case paType of

    SE_PEOPLE_IN_MODEL : index := 1;
  end;
  if index <> -1 then
   if aStatisticFlags[index] then
      Writeln(aStatisticFiles[index].text, FloatToStr(paValue));
end;

procedure TPedSimulation.writeStaticticEvent(paType : integer; paMean,
  paDispersion : real);
var
  index : integer;
begin
  index := -1;
  case paType of
    SE_PEOPLE_IN_MODEL : index := 1;
  end;
  if index <> -1 then
   if aStatisticFlags[index] then
      Writeln(aStatisticFiles[index].text, FloatToStr(paMean) + ':'
      + FloatToStr(paDispersion));
end;


procedure TPedSimulation.logMessage(paMessage: String);
begin
  if aLogger <> nil then aLogger.add(paMessage);
end;

procedure TPedSimulation.Continue;
begin
  inherited Simulation(aLength - CurrentTime);
end;

procedure TPedSimulation.Simulation;
var msg: TPSMessage;
begin
  if aRunProperties = nil
    then begin
      MessageDlg('no run properties loaded!!!',mtError,[mbOK],0);
      exit;
    end;
  if aRunProperties.Stats.Items[PeopleInModel] then
    begin
      aPedCountInModel := TXYStat.create;;
      aPedCountInModel.Reset(0,0);
    end;
  logMessage('length: ' + FloatToStr(aRunProperties.Length));
  TickDelay := TickDuration;
  aLength := aRunProperties.Length;
  msg := TPSMessage.create(self,nil);
  msg.Addressee := AgentBoss;
  msg.Code := mcInit;
  AgentBoss.Manager.Notice(msg);
  try
    inherited Simulation(aLength);
  except
    on E: Exception do MessageDlg(E.Message,mtInformation,[mbOK],0);
  end;
end;

{ TSprava }

constructor TPSMessage.create(paSim: TSimulation; paActor: TPedActor);
begin
  inherited Create(paSim);
  aActor := paActor;
  aID := 0;
  aArrival := nil;
   //inicializacia atributov
end;

function TPSMessage.CreateForm: TMessageForm;
begin
  result := TPSMessage.create(MySim,Actor);
end;

destructor TPSMessage.destroy;
begin
  aArrival := nil;
  aActor := nil;
  inherited;
end;

procedure TPSMessage.MakeCopy(paSample: TMessageForm);
begin
  inherited;
  Actor := (paSample as TPSMessage).Actor;
  ID := (paSample as TPSMessage).ID;
  Arrival := (paSample as TPSMessage).Arrival;
  //atributy naplnit atributmi zo spravy paSample
end;

{TSimLogger}

constructor TSimLogger.create(paStrings: TStrings);
begin
  aStrings := paStrings;
end;

procedure TSimLogger.add(paMessage: String);
begin
  aStrings.Add(paMessage);
  Application.ProcessMessages;
end;

procedure TSimLogger.clear;
begin
  aStrings.Clear;
end;

{TSimGUI}

class function TSimGUI.convertTime(paTime: Double): String;
var
  t: String;
  ct: LongInt;
begin
  ct := Round(paTime);
  t := IntToStr(ct div 3600) + ':';
  ct := ct mod 3600;
  t := t + IntToStr(ct div 60) + ':';
  ct := ct mod 60;
  t := t + IntToStr(ct);
  result := t;
end;

constructor TSimGUI.create(paSim: tSimulation;
                         paDrawFrame: TfEditor;
                         paBar: TProgressBar;
                         paTime: TLabel;
                         paSleepTimeMax: Integer;
                         paSleepTime: Integer;
                         paSleepEveryMax: Integer;
                         paSleepEvery: Integer;
                         paOutput: Boolean);
begin
  aMySim := paSim;
  aDrawFrame := paDrawFrame;
  aSleepEvery := paSleepEvery;
  aSleepEveryMax := paSleepEveryMax;
  aSleepTime := paSleepTime;
  aSleepTimeMax := paSleepTimeMax;
  aOutput := paOutput;
  aBar := paBar;
  aTime := paTime;
end;

destructor TSimGUI.Destroy;
begin
  inherited;
end;

function TSimGUI.getSleepEvery: Cardinal;
begin
  if aSleepEvery = 0
    then result := 1
    else result := aSleepEvery * 60;
end;

function TSimGUI.getSleepTime: Cardinal;
begin
  if aSleepTime = aSleepTimeMax
    then result := 1000
    else result := Round(100 / (aSleepTimeMax - aSleepTime + 1));
end;

procedure TSimGUI.Step;
var
  list : TList<TPedestrian>;
  w : Tscene;
begin
  if (Vypisovat) and (aMySim <> nil) then
    begin
      aTime.Caption := convertTime(MySim.CurrentTime);
      aBar.Position := Round(MySim.CurrentTime);
      w :=(aMySim as TPedSimulation).World;
      aDrawFrame.DrawGLScene(w.Levels[0].AllPedestrians, -1, -1);
      Application.ProcessMessages;
    end;
end;

function TSimGUI.getVypisovat: Boolean;
begin
  result := aOutput;
end;

procedure TSimGUI.setSleepEvery(Value: Cardinal);
begin
  aSleepEvery := Value;
end;

procedure TSimGUI.setSleepTime(Value: Cardinal);
begin
  aSleepTime := Value;
end;

procedure TSimGUI.setVypisovat(Value: Boolean);
begin
  aOutput := Value;
end;

procedure TSimGUI.IncTime;
begin
  if aSleepTime < aSleepTimeMax then Inc(aSleepTime);
end;

procedure TSimGUI.DecTime;
begin
  if aSleepTime > 0 then Dec(aSleepTime);
end;

procedure TSimGUI.IncEvery;
begin
  if aSleepEvery < aSleepEveryMax then Inc(aSleepEvery);
end;

procedure TSimGUI.DecEvery;
begin
  if aSleepEvery > 0 then Dec(aSleepEvery);
end;

end.

